﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MudEngine.Parsing.AST
{
	/// <summary>
	/// Abstract AST Tree super node. All Expressions and Statements inherit from this class.
	/// </summary>
    public abstract class AstNode
    {
		public static ResultType[] COMPATIBLES = new ResultType[] {
			/* UNKNOWN	*/ ResultType.UNKNOWN,
			/* VOID		*/ ResultType.UNKNOWN,
			/* BOOL	    */ ResultType.BOOL,
			/* SHORT    */ ResultType.SHORT,
			/* INT      */ ResultType.SHORT | ResultType.INT,
			/* LONG     */ ResultType.SHORT | ResultType.INT | ResultType.LONG,
			/* STRING	*/ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING | ResultType.VAR,
			/* OBJECT	*/ ResultType.OBJECT,
			/* REAL		*/ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL,
            /* VAR      */ ResultType.FUNCTION | ResultType.VOID | ResultType.BOOL | ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING | ResultType.VAR | ResultType.OBJECT | ResultType.MAP | ResultType.ARRAY,
            /* FUNCTION */ ResultType.FUNCTION,
            /* MAP      */ ResultType.MAP,
            /* ARRAY    */ ResultType.ARRAY
		};

		public static ResultType[] COMPATIBLES_COMPARE = new ResultType[] {
			/* UNKNOWN	*/ ResultType.UNKNOWN,
			/* VOID		*/ ResultType.UNKNOWN,
			/* BOOL	    */ ResultType.BOOL,
			/* SHORT    */ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL,
			/* INT      */ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL,
			/* LONG     */ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL,
			/* STRING	*/ ResultType.STRING,
			/* OBJECT	*/ ResultType.OBJECT,
			/* REAL		*/ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL,
            /* VAR      */ ResultType.VAR,
            /* FUNCTION */ ResultType.FUNCTION,
            /* MAP      */ ResultType.MAP,
            /* ARRAY    */ ResultType.ARRAY
		};

		public static ResultType[] COMPATIBLES_ADD = new ResultType[] {
			/* UNKNOWN	*/ ResultType.UNKNOWN,
			/* VOID		*/ ResultType.UNKNOWN,
			/* BOOL	    */ ResultType.BOOL,
			/* SHORT    */ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING,
			/* INT      */ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING,
			/* LONG     */ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING,
			/* STRING	*/ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING | ResultType.OBJECT | ResultType.VAR | ResultType.MAP | ResultType.ARRAY,
			/* OBJECT	*/ ResultType.OBJECT,
			/* REAL		*/ ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING,
            /* VAR      */ ResultType.VAR,
            /* FUNCTION */ ResultType.FUNCTION,
            /* MAP      */ ResultType.MAP,
            /* ARRAY    */ ResultType.ARRAY | ResultType.BOOL | ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING
		};
		/// <summary>
		/// Holds a reference to the parent node
		/// </summary>
		public AstNode Parent { get; set; }
		/// <summary>
		/// Holds the Return Type of this node
		/// </summary>
		public AType Type { get; set; }
		/// <summary>
		/// The line number in the sourcefile this tree node represents
		/// </summary>
		public int Line { get; set; }
		/// <summary>
		/// The column number in the sourcefile
		/// </summary>
		public int Column { get; set; }
		/// <summary>
		/// Holds all the child nodes
		/// </summary>
        public AstNodeCollection Children { get; private set; }
        /// <summary>
        /// 
        /// </summary>
        public string Annotation { get; set; }
		/// <summary>
		/// 
		/// </summary>
        public AstNode()
        {
			this.Children = new AstNodeCollection();
        }
		/// <summary>
		/// Find all child nodes of a specific type T
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public IEnumerable<T> FindByType<T>()
		{
			return this.Children.OfType<T>();
		}
		/// <summary>
		/// Find all child nodes of a specific type T and predicate
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public IEnumerable<T> FindByType<T>(Func<T, bool> predicate)
        {
            return this.Children.OfType<T>().Where(predicate);
        }
		/// <summary>
		/// Find first child node of a specific type T and predicate.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="recursive">Traverse up the tree to find the specific node type T</param>
		/// <returns></returns>
		public T FindFirstByType<T>(Func<T, bool> predicate, bool recursive)
        {
            T element = this.Children.OfType<T>().FirstOrDefault(predicate);

            if (this.Parent != null && recursive && element == null)
                element = this.Parent.FindFirstByType<T>(predicate, recursive);

            return element;
        }
		/// <summary>
		/// Adds a Tree node to this node
		/// </summary>
		/// <param name="node"></param>
        public void AddNode(AstNode node)
        {
			if (node != null)
			{
				this.Children.Add(node);
				node.Parent = this;
			}
        }
		/// <summary>
		/// Adds a range of nodes to this node
		/// </summary>
		/// <param name="collection"></param>
        public void AddNodes(AstNodeCollection collection)
        {
            foreach(AstNode node in collection)
                this.AddNode(node);
        }

		protected bool CompatibleTypes(ResultType[] collection, ResultType t1, ResultType t2)
		{
            if (t2 == ResultType.UNKNOWN)
                return true;
			return collection[GetBit(t1)].HasFlag(t2);
		}

		protected int GetBit(ResultType type)
		{
			int val = (int)type;
			int count = 0;
			while (val != 0)
			{
				val >>= 1;
				count++;
			}
			return count;
		}

		public abstract void AcceptVisitor<C>(IAstNodeVisitor<C> visitor, C ctx);
    }

	[Flags]
	public enum CheckExpressionResult
	{
		OK,
		LEFT,
		RIGHT
	}
}
